import { useDictionaryStore } from "@/store";
import { type DictionaryModel, enum2Array } from "@/types";
import { isArray } from "@/utils";
import { Form, Select, Space, Tag } from "@arco-design/web-react";
import Pinyin from "pinyin-match";
import {
	type FC,
	type Key,
	type ReactElement,
	useCallback,
	useEffect,
	useState,
} from "react";

// biome-ignore lint/suspicious/noExplicitAny: <explanation>
type SelectDictionaryValueType = any | any[];

interface OptionItem {
	label: string;
	// biome-ignore lint/suspicious/noExplicitAny: <explanation>
	value: any;
}

interface SelectDictionaryProps {
	id?: string;
	value?: SelectDictionaryValueType;
	type?: "dropdown" | "tag-list";
	multiple?: boolean;
	/** 后端字典模块名称 */
	module?: string;
	/** 选项列表 */
	options?: Array<DictionaryModel>;
	/** 枚举类型 */
	enumType?: unknown;
	/** 是否显示"全部"选项 */
	showAllOption?: boolean;
	/** "全部"选项的文本 */
	allOptionText?: string;
	/** "全部"选项的值 */
	// biome-ignore lint/suspicious/noExplicitAny: <explanation>
	allOptionValue?: any;
	allowClear?: boolean;
	allowSearch?: boolean;
	placeholder?: string;
	disabled?: boolean;
	onChange?: (value?: SelectDictionaryValueType) => void;
	onSelectedChange?: (
		option: DictionaryModel | DictionaryModel[] | null,
	) => void;
}

const SelectDictionary: FC<SelectDictionaryProps> = ({
	id,
	value,
	type = "dropdown",
	multiple = false,
	module,
	options,
	enumType,
	showAllOption = false,
	allOptionText = "全部",
	allOptionValue = null,
	allowClear = true,
	allowSearch = false,
	placeholder = "请选择",
	disabled = false,
	onChange,
	onSelectedChange,
}) => {
	const { get: getModule } = useDictionaryStore();
	const [data, setData] = useState<OptionItem[]>([]);

	const { disabled: formDisabled } = Form.useFormContext();

	const mergedDisabled = disabled !== undefined ? disabled : formDisabled;

	const convertOptionToDictionary = useCallback(
		(option: OptionItem): DictionaryModel => {
			return {
				text: option.label,
				value: option.value,
			};
		},
		[],
	);

	const convertDictionaryToOption = useCallback(
		(dictionary: DictionaryModel): OptionItem => {
			return {
				label: dictionary.text,
				value: dictionary.value,
			};
		},
		[],
	);

	const handleChange = (v: SelectDictionaryValueType) => {
		onChange?.(v);
		if (onSelectedChange) {
			if (v == null) {
				onSelectedChange(null);
			} else {
				if (multiple && isArray(v)) {
					const selectedItems = data.filter(
						(x) => v.indexOf(x.value as never) >= 0,
					);
					onSelectedChange(selectedItems.map(convertOptionToDictionary));
				} else {
					const selectedItem = data.find((x) => x.value === value);
					onSelectedChange(
						selectedItem ? convertOptionToDictionary(selectedItem) : null,
					);
				}
			}
		}
	};

	const isTagChecked = (item: OptionItem) => {
		if (isArray(value)) return value.some((i) => i === item.value);
		return value === item.value;
	};

	const handleTagCheck = (item: OptionItem, checked: boolean) => {
		if (isArray(value)) {
			const newValues = [...value];
			if (checked) newValues.push(item.value);
			else {
				const index = newValues.findIndex((i) => i === item.value);
				newValues.splice(index, 1);
				if (newValues.length === 0) {
					newValues.push(allOptionValue);
				}
			}
			handleChange(newValues);
		} else {
			const currentChecked = isTagChecked(item);
			if (currentChecked) return;

			handleChange(item.value);
		}
	};

	const handleSearch = (inputValue: string, option: ReactElement) => {
		return Pinyin.match(
			(option as unknown as OptionItem).label,
			inputValue,
		) as boolean;
	};

	useEffect(() => {
		const withAllOption = (items: Array<DictionaryModel>) => {
			if (!showAllOption) return items.map(convertDictionaryToOption);
			return [
				{
					label: allOptionText,
					value: allOptionValue,
				},
				...items.map(convertDictionaryToOption),
			];
		};

		const loadOptions = async () => {
			if (options) setData(withAllOption(options));
			else if (module) {
				const moduleData = await getModule(module);
				setData(withAllOption(moduleData));
			} else if (enumType) {
				// biome-ignore lint/suspicious/noExplicitAny: <explanation>
				setData(withAllOption(enum2Array(enumType as Record<string, any>)));
			}
		};

		loadOptions();
	}, [
		allOptionText,
		allOptionValue,
		enumType,
		getModule,
		module,
		options,
		showAllOption,
		convertDictionaryToOption,
	]);

	return (
		<div id={id}>
			{type === "dropdown" ? (
				<Select
					value={value}
					mode={multiple ? "multiple" : undefined}
					options={data}
					allowClear={allowClear}
					showSearch={allowSearch}
					placeholder={placeholder}
					disabled={mergedDisabled}
					filterOption={handleSearch}
					onChange={handleChange}
				/>
			) : (
				<Space align="center">
					{data.map((x) => (
						<Tag
							key={x.value as Key}
							checkable={!mergedDisabled}
							checked={x.value === value}
							onCheck={(checked) => handleTagCheck(x, checked)}
						>
							{x.label}
						</Tag>
					))}
				</Space>
			)}
		</div>
	);
};

export default SelectDictionary;
